#!/usr/bin/python

import xml.etree.ElementTree as ET
import os
import requests
import time
import yaml
import sys
import argparse
from datetime import datetime
import random

# input:
# no input required, one game by system will be tested. you can choose a list if you create a file /userdata/system/gamesToTest.yml, example:
# fbneo: [
#     "/userdata/roms/fbneo/dino.zip",
#     "/userdata/roms/fbneo/sfa3.zip"
#   ]
# megadrive: [
#     "/userdata/roms/megadrive/Sonic The Hedgehog.md",
#     "/userdata/roms/megadrive/Bonanza Bros.md",
#     "/userdata/roms/megadrive/Comix Zone.md"
#   ]
# psx: [
#     "/userdata/roms/psx/Road Rash.PBP"
#   ]
#
# output:
# /userdata/reportTest/batocera-test_22-07-03_18h34.42/report.txt

class BatoceraTest():

    @staticmethod
    def run(args):
        timeGame = 15
        screenshot = True
        outputDir = "/userdata/reportTest/"
        configFile = "/userdata/system/gamesToTest.yml"

        args = BatoceraTest.parseCommandLine(args)

        if (args.time):
            timeGame = args.time[0]

        if (args.screenshot):
            screenshot = args.screenshot

        emulators = BatoceraTest.findEmulators()

        # take randomly some emulators
        l = list(emulators.keys())
        random.shuffle(l)
        l = l[:2]
        emulators_tmp = {}
        for e in l:
            emulators_tmp[e] = emulators[e]
        emulators = emulators_tmp
        ###

        if os.path.exists(configFile):
            games = BatoceraTest.findGamesForEmulatorsFromFile(emulators, configFile)
        else:
            games = BatoceraTest.findGamesForEmulators(emulators)

        resultName = "batocera-test_" + datetime.now().strftime('%y-%m-%d_%Hh%M.%S')
        resultDir = outputDir + resultName
        try:
            print(f"creating directory {resultDir}")
            os.makedirs(resultDir)
        except:
            raise Exception(f"unable to create the directory {resultDir}")

        results = []
        for system in games:
            for game in games[system]:
                if BatoceraTest.runTest(system, game, resultDir, timeGame, screenshot):
                    print("Status: \033[01;32mSUCCESS\033[00;37m\n")
                    results.append({"system": system, "game": game, "status": True})
                else:
                    print("Status: \033[01;31mFAILED\033[00;37m\n")
                    results.append({"system": system, "game": game, "status": False})

        # generate report
        BatoceraTest.gameReport(resultDir, results)

    @staticmethod
    def gameReport(resultDir, results):
        with open(resultDir+"/report.txt", "w") as f:
            f.write("  Status      System            Game\n")
            f.write("=======================================================================\n")
            for result in results:
                if result["status"]:
                    f.write("[SUCCESS]   {:17} {}\n".format(result["system"], os.path.basename(result["game"])))
                else:
                    f.write("[FAILED]    {:17} {}\n".format(result["system"], os.path.basename(result["game"])))

    @staticmethod
    def runTest(system, game, resultDir, timeGame, screenshot):
        isSuccess = False
        print("System: {}".format(system))
        print("Game: {}".format(os.path.basename(game)))

        # start the game
        # curl http://localhost:1234/launch -d "/userdata/roms/gamecube/Super Mario Sunshine.iso"
        print(f"launching game '{game}'")
        x = requests.post("http://localhost:1234/launch", data = game)
        if not x.ok:
            raise Exception("fail call es")
        print(f"waiting {timeGame}s in game")
        time.sleep(timeGame)

        try:
            print(f"checking that the game is still running")
            x = requests.get("http://localhost:1234/runningGame")
            if not x.ok:
                raise Exception("fail call es")
            
            xj = x.json()
            if "systemName" in xj:
                stillRunning = (xj["systemName"] == system)
            else:
                stillRunning = False
        except Exception as e:
            print(e)
            stillRunning = False

        if stillRunning:
            print(f"taking a screenshot")
            if (screenshot):
                BatoceraTest.screenshot(resultDir, system)
                isSuccess = True # yep, we manage to take a screenshot of the game
                time.sleep(2) # delay to take a screenshot

        # stop the game
        print(f"exiting the game")
        code = os.system("hotkeygen --send exit")

        while stillRunning:
            print(f"waiting 10 seconds")
            time.sleep(10) # delay to quit
            x = requests.get("http://localhost:1234/runningGame")
            if not x.ok:
                raise Exception("fail call es")
            xj = x.json()
            if "systemName" in xj:
                stillRunning = (xj["systemName"] == system)
            else:
                stillRunning = False
        
        print(f"end of test")

        return isSuccess

    @staticmethod
    def findEmulators():
        emulators = {}
        if os.path.exists("/userdata/system/configuration/emulationstation/es_systems.cfg"):
            tree = ET.parse("/userdata/system/configuration/emulationstation/es_systems.cfg")
        elif os.path.exists("/usr/share/emulationstation/es_systems.cfg"):
            tree = ET.parse("/usr/share/emulationstation/es_systems.cfg")
        else:
            raise Exception("es_system.cfg not found");
        systemList = tree.getroot()

        if systemList.tag != "systemList":
            raise Exception("root tag is not systemList");
        for system in systemList:
            if system.tag == "system":
                infos = {}
                for systemInfo in system:
                    if systemInfo.tag in ["name", "path", "extension"]:
                        infos[systemInfo.tag] = systemInfo.text
                if "name" in infos and "path" in infos and "extension" in infos:
                    emulators[infos["name"]] = {"path": infos["path"], "extensions": infos["extension"].split(" ")}
        return emulators

    @staticmethod
    def findGamesForEmulators(emulators):
        games = {}
        for emulator in emulators:
            game = BatoceraTest.findGamesForEmulator(emulator, emulators[emulator])
            if game is not None:
                games[emulator] = [game]
        return games

    @staticmethod
    def findGamesForEmulator(emulator, infos):
        for root, dirs, files in os.walk(infos["path"]):
            if "/roms/" in root:
                for file in files:
                    for extension in infos["extensions"]:
                        if file.endswith(extension):
                            return os.path.join(root, file)
        return None

    @staticmethod
    def findGamesForEmulatorsFromFile(emulators, file):
        with open(file, 'r') as f:
            testgames = yaml.load(f, Loader=yaml.CLoader)
        games = {}
        for system in testgames:
            games[system] = testgames[system]
        return games

    @staticmethod
    def screenshot(resultDir, name):
        resultDir = "{}/screenshots/".format(resultDir)
        if not os.path.exists(resultDir):
            os.makedirs(resultDir)

        name = os.path.splitext(name)[0]
        pathScreenshot = "batocera-screenshot {}/{}.png".format(resultDir, name)
        os.system(pathScreenshot)
        print(f"Screenshot ({pathScreenshot})")

    @staticmethod
    def parseCommandLine(args):

        parser = argparse.ArgumentParser(description='Testing Batocera emulation systems.')

        parser.add_argument('-t', '--time', nargs=1, type=int,
                            help='Time in seconds that the game runs. (default=15)')

        parser.add_argument('-s', '--screenshot', dest='screenshot', action='store_true',
                            help='Screenshot of the game running. (default=false)')

        args = parser.parse_args()

        return args

if __name__ == '__main__':
   BatoceraTest.run(sys.argv[1:])
